import React, { useEffect, useState, useRef } from 'react';
import styles from './index.less';
import './index.less';
import G6 from '@antv/g6';
import { rawData, cavasConfig } from './data.ts';

const Index = () => {
  const { Util } = G6;
  // 定义画布
  let canvasTree: any;
  // 选择节点Id
  const activeRef: any = useRef(null);
  // 选中节点收缩状态
  const collapsedRef: any = useRef(null);
  // 画布树形节点数据
  let rawDataArr: any = [rawData];
  // 用于接收选中节点信息
  const evtArrIsRef: any = useRef(null);
  // 弹窗显示状态
  const [isModalOpen, setIsModalOpen] = useState<boolean>(false);

  // 初始化
  useEffect(() => {
    // 获取容器元素并创建一个包含说明文字的 <pre> 元素
    const container = document.getElementById('container') as HTMLElement;
    // 设置画布的宽度和高度
    const width = container.scrollWidth;

    // 创建一个 G6 树图实例，并配置参数
    canvasTree = new G6.TreeGraph({
      ...cavasConfig,
      width, // 设置宽度
    });

    disableDefaultContextMenu(container);
    contextmenuFn();
    isMouseOutsideElement();

    // 加载数据并渲染树图
    canvasTree.data(dataTransform(rawData));

    // 渲染树图
    canvasTree.render();
  }, []);

  // 注册根节点类型
  G6.registerNode(
    'dice-mind-map-root',
    {
      jsx: (cfg) => {
        const width = Util.getTextSize(cfg.label, 16)[0] + 24;
        const stroke = cfg.style.stroke || '#5B8FF9';
        const marginLeft = width / 2 - 20 / 2;

        return `
                <group>
                    <rect draggable="true" style={{width: ${width}, height: 70, stroke: ${stroke}, fill: '#C6E5FF', radius: 4,display:flex, flexDirection: 'column'}} keyshape>
                        <image
                            style={{
                                img: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Q_FQT6nwEC8AAAAAAAAAAABkARQnAQ',
                                width: 20,
                                height: 20,
                                marginLeft: ${marginLeft}, 
                                marginTop: 10
                            }}
                        />    
                        <text style={{ fontSize: 16, marginLeft: 10, marginTop: 20 }}>${cfg.label}</text>
                    </rect>
                </group>
            `;
      },

      getAnchorPoints() {
        return [
          [0, 0.5],
          [1, 0.5],
        ];
      },
    },
    'single-node',
  );

  /**
   * 注册一个行为 'scroll-canvas'，用于处理画布的滚动事件。
   * 当用户滚动鼠标滚轮时，根据情况调整画布的缩放或平移。
   * 如果按下 Ctrl 键并滚动鼠标滚轮，则缩放画布。
   * 否则，根据鼠标滚轮的移动距离来平移画布。
   * @returns {object} 返回包含滚动事件处理函数的对象。
   */
  G6.registerBehavior('scroll-canvas', {
    // 定义获取事件的方法，返回一个包含 wheel 事件处理函数的对象
    getEvents: function getEvents() {
      return {
        wheel: 'onWheel', // 当发生滚轮事件时，调用 onWheel 方法处理
      };
    },
    // 处理滚轮事件的方法
    onWheel: function onWheel(ev) {
      const { graph } = this; // 获取当前行为关联的图形实例
      if (!graph) {
        return; // 如果图形实例不存在，则直接返回
      }

      if (ev.ctrlKey) {
        // 如果同时按下了 Ctrl 键
        const canvas = graph.get('canvas'); // 获取画布实例
        const point = canvas.getPointByClient(ev.clientX, ev.clientY); // 根据鼠标位置获取画布上的点坐标

        let ratio = graph.getZoom(); // 获取当前缩放比例
        if (ev.wheelDelta > 0) {
          // 如果滚轮向上滚动
          ratio += ratio * 0.05; // 增加缩放比例
        } else {
          // 如果滚轮向下滚动
          ratio *= ratio * 0.05; // 减小缩放比例
        }

        // 缩放图形到指定的缩放比例，并以指定点为中心进行缩放
        graph.zoomTo(ratio, {
          x: point.x,
          y: point.y,
        });
      } else {
        // 如果没有按下 Ctrl 键
        const x = ev.deltaX || ev.movementX; // 获取水平方向的滚动距离
        const y = ev.deltaY || ev.movementY || (-ev.wheelDelta * 125) / 3; // 获取垂直方向的滚动距离

        // 平移图形的视口位置
        graph.translate(-x, -y);
      }

      ev.preventDefault(); // 阻止默认滚动行为
    },
  });

  /**
   * 函数转换数据
   * @param data 节点数据
   * @returns
   */
  const dataTransform = (data: []) => {
    // 定义递归函数 changeData，用于转换数据结构和添加属性
    const changeData = (d, level = 0, color) => {
      // 创建一个新的数据对象，保留原始数据不变
      const data = {
        ...d,
      };
      data.type = 'dice-mind-map-root';
      // 如果当前节点有子节点，则递归处理每个子节点
      if (d.children) {
        data.children = d.children.map((child) =>
          changeData(child, level + 1, data.color),
        ); // 递归处理子节点数据
      }
      return data; // 返回处理后的数据对象
    };

    return changeData(data); // 调用递归函数，返回整体转换后的数据
  };

  /**
   * 鼠标右键--需要显示选项卡，根据坐标系显示位置
   * @param x
   * @param y
   */
  function showModal(x: number, y: number) {
    const modal: any = document.getElementById('my-modal');
    modal.style.display = 'block';
    modal.style.left = x + 'px';
    modal.style.top = y + 'px';
  }

  // 隐藏模态框
  function hideModal() {
    const modal: any = document.getElementById('my-modal');
    modal.style.display = 'none';
  }

  /**
   * 禁用画布的默认右键菜单
   * @param  container 画布元素
   */
  const disableDefaultContextMenu = (container: any) => {
    container.addEventListener('contextmenu', (event: Event) => {
      event.preventDefault();
    });
  };
  /**
   * 节点鼠标右键触发函数
   */
  const contextmenuFn = () => {
    // 监听右键事件
    canvasTree.on('node:contextmenu', (evt: any) => {
      const { canvasX, canvasY } = evt;
      activeRef.current = evt.item._cfg.id;
      evtArrIsRef.current = evt;
      showModal(canvasX, canvasY);
    });
  };
  /**
   * 节点右键选中后的弹窗--鼠标移出去后隐藏
   */
  const isMouseOutsideElement = () => {
    const modelElement = document.getElementById('my-modal') as HTMLElement;

    // 监听画布的鼠标移动事件
    modelElement.addEventListener('mouseleave', (evt) => {
      // 否则隐藏模态框
      hideModal();
    });
  };

  /**
   * 节点展开收缩
   * @param status 1:收缩 2:展开 3: 新增 4:删除
   */
  const isCollapsedExpanded = (status: number) => {
    hideModal();
    const { item, currentTarget } = evtArrIsRef.current;
    if (status === 1) {
      collapsedRef.current = true;
      // 更新画布中某个节点的属性并重新布局图，第二个参数是节点对象
      currentTarget.updateItem(item, {
        collapsed: collapsedRef.current,
      });
    } else if (status === 2) {
      collapsedRef.current = false;
      // 更新画布中某个节点的属性并重新布局图，第二个参数是节点对象
      currentTarget.updateItem(item, {
        collapsed: collapsedRef.current,
      });
    } else if (status === 3) {
      // 获取节点模型数据
      const model = item.get('model');
      // 节点id
      const newId =
        model.id +
        '-' +
        (((model.children || []).reduce((a, b) => {
          const num = Number(b.id.split('-').pop());
          return a < num ? num : a;
        }, 0) || 0) +
          1);
      currentTarget.updateItem(item, {
        children: (model.children || []).concat([
          {
            id: newId,
            // 设置新节点标签
            label: '新增节点',
            // 设置新节点类型
            type: 'dice-mind-map-root',
          },
        ]),
      });
    }
    // 弹窗
    else if (status === 4) {
      // 获取节点模型数据
      const model = item.get('model');
      // 获取父节点
      const parent = item.get('parent');
      currentTarget.updateItem(parent, {
        children: (parent.get('model').children || []).filter(
          (e) => e.id !== model.id,
        ),
      });
    } else if (status === 5) {
      isModalOpenFn();
    }
    // 局部更新画布
    evtArrIsRef.current.currentTarget.layout(false);
  };

  /**
   * 模态框组件---打开，关闭
   */
  const isModalOpenFn = () => {
    setIsModalOpen(!isModalOpen);
  };

  return (
    <div className={styles.box}>
      <div id="container" />
      <div id="my-modal" className={styles.customPopup}>
        <div
          onClick={() => {
            isCollapsedExpanded(1);
          }}
          className={styles.customPopupText}
        >
          收缩节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(2);
          }}
          className={styles.customPopupText}
        >
          展开节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(3);
          }}
          className={styles.customPopupText}
        >
          新增节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(4);
          }}
          className={styles.customPopupText}
        >
          删除节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(5);
          }}
          className={styles.customPopupText}
        >
          节点交互
        </div>
      </div>
    </div>
  );
};
export default Index;
